using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Sirenix.OdinInspector;
using UnityEditor;
using UnityEngine;

namespace LFromeWork
{
    [CreateAssetMenu(menuName = "LAsset/AssetBundleSettings")]
    public class AssetBundleSettings : ScriptableObject
    {
        #region 定义的 type 和 class

        // 目标平台枚举
        public enum BuildTargetType
        {
            Windows,
            Android,
            IOS
        }

        [Serializable] //AssetBundle 分类
        public class AssetBundleCategory
        {
            [LabelText("名字"), LabelWidth(30)] public string Name;

            /// <summary>
            /// AssetBundle信息集合
            /// </summary>
            public AssetBundleData[] Datas;
        }

        [Serializable] //AssetBundle信息类
        public class AssetBundleData
        {
            [FolderPath(ParentFolder = "Assets")] public string Path;

            [LabelText("文件夹为一个AB包")] public bool Overall;

            [LabelText("是否初始资源")] public bool IsFirstData;
        }

        #endregion

        #region 普通变量定义

        /// <summary>
        /// 要收集的资源包
        /// </summary>
        private List<AssetBundleBuild> builds = new List<AssetBundleBuild>();

        /// <summary>
        /// 临时目录
        /// </summary>
        public string TempPath
        {
            get
            {
                return Application.dataPath + "/../" + AssetBundleSavePath + "/" + AssetVersion + "_Temp/" +
                       CurrBuildTarget;
            }
        }

        /// <summary>
        /// 输出目录
        /// </summary>
        public string OutPath
        {
            get { return TempPath.Replace("_Temp", ""); }
        }

        #endregion

        #region 布局显示 变量定义

        [HorizontalGroup("Common", LabelWidth = 75)] [VerticalGroup("Common/Left")] [LabelText("资源版本号")]
        public string AssetVersion = "1.0.0";

        [PropertySpace(10)] [VerticalGroup("Common/Left")] [LabelText("目标平台")]
        public BuildTargetType CurrBuildTarget;

        [PropertySpace(10)] [VerticalGroup("Common/Left")] [LabelText("打包参数")]
        public BuildAssetBundleOptions Options;

        //打AB包
        [VerticalGroup("Common/Right")]
        [Button(ButtonSizes.Medium)]
        public void BuildAssetBundle()
        {
            //收集 AssetBundleBuild
            CollectAssetBundleBuild();

            Debug.Log("builds count == " + builds.Count);
            //没有需要打包 的 返回
            if (builds.Count == 0) return;

            //不存在 ab存放文件夹 创建
            if (!Directory.Exists(TempPath)) Directory.CreateDirectory(TempPath);

            //开始打ab包
            BuildPipeline.BuildAssetBundles(TempPath, builds.ToArray(), Options, GetBuildTarget());
            Debug.Log("资源包打包完毕");

            //拷贝 临时文件夹 到 正式文件夹
            CopyTempToFormal(TempPath, OutPath);
            Debug.Log("ab拷贝到 正式 文件夹完毕");

            CreateDependenciesFile();
            Debug.Log("AssetInfo生成依赖关系文件完毕");

            CreateVersionFile();
            Debug.Log("VersionInfo生成版本文件完毕");
        }

        //赋值AB到Streamming文件夹下
        [VerticalGroup("Common/Right")]
        [Button(ButtonSizes.Medium)]
        public void CopyABToStreamming()
        {
            string sourceDirName = OutPath;
            if (!Directory.Exists(sourceDirName))
                EditorUtility.DisplayDialog("", "源文件夹不存在", "确定");

            string destDirName = Application.streamingAssetsPath + "/" + AssetBundleSavePath + "/";
            if (Directory.Exists(destDirName))
                Directory.Delete(destDirName, true);
            Directory.CreateDirectory(destDirName);

            string[] dirs = Directory.GetDirectories(sourceDirName);
            foreach (string dir in dirs)
            {
                IOUtil.CopyDirectory(dir, destDirName + Path.GetFileName(dir));
            }

            string[] files = Directory.GetFiles(sourceDirName);
            foreach (string file in files)
            {
                if (File.Exists(destDirName + Path.GetFileName(file)))
                    continue;
                FileInfo fileInfo = new FileInfo(file);
                if (fileInfo.Name.Equals(CurrBuildTarget.ToString(), StringComparison.CurrentCultureIgnoreCase)
                    || fileInfo.FullName.Contains("AssetInfo.json", StringComparison.CurrentCultureIgnoreCase)
                    || fileInfo.FullName.Contains("VersionFile.txt", StringComparison.CurrentCultureIgnoreCase)
                   )
                    continue;

                File.Copy(file, destDirName + Path.GetFileName(file), true);
                File.SetAttributes(destDirName + Path.GetFileName(file), FileAttributes.Normal);
            }

            EditorUtility.DisplayDialog("", "Copy 完成", "确定");
        }

        //和 Assets文件夹同级
        [LabelText("AB保存路径")] public string AssetBundleSavePath;

        [LabelText("勾选进行编辑")] public bool IsCanEditor;

        [EnableIf("IsCanEditor")] [BoxGroup("AssetBundleSettings")]
        public AssetBundleCategory[] CategoryInfos;

        #endregion

        /// <summary>
        /// 获取选择的 打ab 平台
        /// </summary>
        /// <returns></returns>
        public BuildTarget GetBuildTarget()
        {
            switch (CurrBuildTarget)
            {
                default:
                case BuildTargetType.Windows:
                    return BuildTarget.StandaloneWindows64;
                case BuildTargetType.Android:
                    return BuildTarget.Android;
                case BuildTargetType.IOS:
                    return BuildTarget.iOS;
            }
        }

        #region 打包ab相关

        /// <summary>
        /// 收集 AssetBundleBuild
        /// </summary>
        private void CollectAssetBundleBuild()
        {
            builds.Clear();
            for (int i = 0; i < CategoryInfos.Length; i++)
            {
                AssetBundleCategory category = CategoryInfos[i];
                for (int j = 0; j < category.Datas.Length; j++)
                {
                    BuildAssetBundleForPath(category.Datas[j]);
                }
            }
        }

        /// <summary>
        /// 根据路径信息打包资源
        /// </summary>
        private void BuildAssetBundleForPath(AssetBundleData abData)
        {
            string fullPath = Application.dataPath + "/" + abData.Path;

            DirectoryInfo directoryInfo = new DirectoryInfo(fullPath);
            FileInfo[] files = directoryInfo.GetFiles("*", SearchOption.AllDirectories);
            string[] validPathArr = GetValidFilePath(files);

            if (abData.Overall)
            {
                AddAssetBundleBuild(abData.Path, validPathArr);
            }
            else
            {
                string validPath = null;
                for (int i = 0; i < validPathArr.Length; i++)
                {
                    validPath = validPathArr[i];

                    AddAssetBundleBuild(validPath.Replace("Assets/", ""), new string[] { validPath });
                }
            }
        }

        /// <summary>
        /// 添加 AssetBundleBuild
        /// </summary>
        private void AddAssetBundleBuild(string assetBundleName, string[] assetNames)
        {
            AssetBundleBuild abBuild = new AssetBundleBuild();
            // ab名字随便，不需要包含Assets
            abBuild.assetBundleName = assetBundleName + ".ab";
            //aseet名字 需要包含Assets/ 就是从工程开始算的路径
            abBuild.assetNames = assetNames;
            builds.Add(abBuild);
        }

        /// <summary>
        /// 获取有效文件路径
        /// </summary>
        /// <param name="fileArr"></param>
        /// <returns></returns>
        private string[] GetValidFilePath(FileInfo[] fileArr)
        {
            List<string> pathList = new List<string>();

            FileInfo fileInfo = null;
            for (int i = 0; i < fileArr.Length; i++)
            {
                fileInfo = fileArr[i];
                if (!fileInfo.Extension.Equals(".meta", StringComparison.CurrentCultureIgnoreCase))
                    pathList.Add("Assets" + fileInfo.FullName.Replace("\\", "/").Replace(Application.dataPath, ""));
            }

            return pathList.ToArray();
        }

        /// <summary>
        /// 拷贝临时
        /// </summary>
        /// <param name="oldPath"></param>
        private void CopyTempToFormal(string oldPath, string newPath)
        {
            if (Directory.Exists(newPath))
                Directory.Delete(newPath, true);
            //拷贝
            IOUtil.CopyDirectory(oldPath, newPath);
            //删除临时文件夹
            Directory.Delete(oldPath, true);
        }

        #endregion

        #region 创建依赖文件相关

        /// <summary>
        /// 创建依赖文件（AssetInfo）
        /// </summary>
        private void CreateDependenciesFile()
        {
            //生成 AssetInfo文件的时候用
            List<AssetInfoEntity> tempLst = new List<AssetInfoEntity>();
            //当获取 asset 依赖哪些ab 的时候用,容易找到
            Dictionary<string, AssetInfoEntity> tempDic = new Dictionary<string, AssetInfoEntity>();

            //获取所有打ab包的文件 AssetInfoEntity
            for (int i = 0; i < CategoryInfos.Length; i++)
            {
                AssetBundleCategory category = CategoryInfos[i];
                for (int j = 0; j < category.Datas.Length; j++)
                {
                    string path = Application.dataPath + "/" + category.Datas[j].Path;
                    CollectAssetFileInfo(tempLst, tempDic, path);
                }
            }

            //获取依赖的 assetBundle
            CollectDependenciesAssetBundle(tempLst, tempDic);

            //生成一个Json文件
            string targetPath = OutPath;
            if (!Directory.Exists(targetPath)) Directory.CreateDirectory(targetPath);

            string strJsonFilePath = targetPath + "/AssetInfo.json"; //依赖文件路径
            IOUtil.CreateTextFile(strJsonFilePath, tempLst.ToJson());

            //生成二进制文件
            MMO_MemoryStream ms = new MMO_MemoryStream();
            ms.WriteInt(tempLst.Count);
            for (int i = 0; i < tempLst.Count; i++)
            {
                AssetInfoEntity entity = tempLst[i];
                ms.WriteUTF8String(entity.AssetFullPath);
                ms.WriteUTF8String(entity.AssetBundleFullPath);

                if (entity.DependsAssetBundleList != null)
                {
                    //添加依赖资源
                    int depLen = entity.DependsAssetBundleList.Count;
                    ms.WriteInt(depLen);
                    for (int j = 0; j < depLen; j++)
                    {
                        ms.WriteUTF8String(entity.DependsAssetBundleList[j]);
                    }
                }
                else
                {
                    ms.WriteInt(0);
                }
            }

            string filePath = targetPath + "/AssetInfo.bytes"; //版本文件路径
            byte[] buffer = ms.ToArray();
            buffer = ZlibHelper.CompressBytes(buffer);
            using (FileStream fs = new FileStream(filePath, FileMode.Create))
            {
                fs.Write(buffer, 0, buffer.Length);
            }
        }

        /// <summary>
        /// 收集文件 AssetInfo 对象
        /// </summary>
        /// <param name="tempLst"></param>
        /// <param name="tempDic"></param>
        /// <param name="folderPath"></param>
        private void CollectAssetFileInfo(List<AssetInfoEntity> tempLst, Dictionary<string, AssetInfoEntity> tempDic,
            string folderPath)
        {
            DirectoryInfo dirInfo = new DirectoryInfo(folderPath);
            if (dirInfo.Exists == false) return;

            FileInfo[] fileInfos = dirInfo.GetFiles("*", SearchOption.AllDirectories);

            for (int i = 0; i < fileInfos.Length; i++)
            {
                FileInfo file = fileInfos[i];
                if (file.Extension.Equals(".meta")) continue;

                AssetInfoEntity infoEntity = new AssetInfoEntity();

                string fullPath = file.FullName;

#if UNITY_EDITOR_WIN
                infoEntity.AssetFullPath = fullPath.Substring(fullPath.IndexOf("Assets\\")).Replace("\\", "/");
#else
                infoEntity.AssetFullPath = fullPath.Substring(fullPath.IndexOf("Assets/"));
#endif

                infoEntity.AssetBundleFullPath = GetAssetBundleName(infoEntity.AssetFullPath).ToLower() + ".ab";

                tempLst.Add(infoEntity);
                tempDic[infoEntity.AssetFullPath] = infoEntity;
            }
        }

        /// <summary>
        /// 获取assetBundle 名字，更具asset路径
        /// </summary>
        private string GetAssetBundleName(string assetFullName)
        {
            for (int i = 0; i < CategoryInfos.Length; i++)
            {
                AssetBundleCategory category = CategoryInfos[i];
                for (int j = 0; j < category.Datas.Length; j++)
                {
                    AssetBundleData abData = category.Datas[j];
                    if (assetFullName.IndexOf(abData.Path, StringComparison.CurrentCultureIgnoreCase) > -1)
                    {
                        if (abData.Overall)
                            return abData.Path.ToLower();
                        else
                            return assetFullName.Replace("Assets/", "");
                    }
                }
            }

            return null;
        }

        /// <summary>
        /// 获取asset 依赖的 assetBundle
        /// </summary>
        /// <param name="tempLst"></param>
        /// <param name="tempDic"></param>
        private void CollectDependenciesAssetBundle(List<AssetInfoEntity> tempLst,
            Dictionary<string, AssetInfoEntity> tempDic)
        {
            for (int i = 0; i < tempLst.Count; i++)
            {
                AssetInfoEntity entity = tempLst[i];

                entity.DependsAssetBundleList = new List<string>();
                string[] arr = AssetDatabase.GetDependencies(entity.AssetFullPath, true);
                foreach (string str in arr)
                {
                    if (!str.IsSuffix(".cs") && tempDic.ContainsKey(str))
                    {
                        //把多余的依赖AB包剔除掉，比如依赖AB包==主AB包， 或者依赖AB包已经存在于DependsAssetBundleList内
                        if (!entity.AssetBundleFullPath.Equals(tempDic[str].AssetBundleFullPath) &&
                            !entity.DependsAssetBundleList.Contains(tempDic[str].AssetBundleFullPath))
                        {
                            //把依赖资源 加入到依赖资源列表
                            entity.DependsAssetBundleList.Add(tempDic[str].AssetBundleFullPath);
                        }
                    }
                }
            }
        }

        #endregion

        #region 创建版本文件

        /// <summary>
        /// 创建版本文件
        /// </summary>
        private void CreateVersionFile()
        {
            string path = OutPath;
            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);

            StringBuilder sbContent = new StringBuilder();

            //创建 版本号文件
            CreateVersionNumberFile();

            //创建用来 查看的json 版本文件
            CreateVersionJsonFile(path, sbContent);

            //创建用来 程序内检查版本更新 的byte文件
            CreateVersionByteFile(path, sbContent);
        }

        /// <summary>
        /// 创建 版本号 文件
        /// </summary>
        private void CreateVersionNumberFile()
        {
            //版本号文件路径
            string strVersionNumberPath = OutPath + "/VersionNumber.txt" ;
            IOUtil.CreateTextFile(strVersionNumberPath, AssetVersion);
        }

        /// <summary>
        /// 创建VersionInfo.json版本文件
        /// </summary>
        /// <param name="path"></param>
        /// <param name="sbContent"></param>
        private void CreateVersionJsonFile(string path, StringBuilder sbContent)
        {
            string strVersionFilePath = path + "/VersionFile.txt"; //版本文件路径

            //如果版本文件存在 则删除
            IOUtil.DeleteFile(strVersionFilePath);

            DirectoryInfo directory = new DirectoryInfo(path);

            //拿到文件夹下所有文件
            FileInfo[] arrFiles = directory.GetFiles("*", SearchOption.AllDirectories);

            for (int i = 0; i < arrFiles.Length; i++)
            {
                FileInfo file = arrFiles[i];

                if (file.Extension == ".manifest" || file.Extension == ".json" ||
                    file.Name.Equals(CurrBuildTarget.ToString()))
                {
                    continue;
                }

                string fullName = file.FullName; //全名 包含路径扩展名

                //相对路径
                string name = fullName.Substring(fullName.IndexOf(CurrBuildTarget.ToString()) +
                                                 CurrBuildTarget.ToString().Length + 1);

                string md5 = EncryptUtil.GetFileMD5(fullName); //文件的MD5
                if (md5 == null) continue;

                string size = file.Length.ToString(); //文件大小

                bool isFirstData = false; //是否初始数据
                bool isBreak = false;

                for (int j = 0; j < CategoryInfos.Length; j++)
                {
                    foreach (AssetBundleData abData in CategoryInfos[j].Datas)
                    {
                        string tempPath = abData.Path;

                        name = name.Replace("\\", "/");
                        if (name.IndexOf(tempPath, StringComparison.CurrentCultureIgnoreCase) != -1)
                        {
                            isFirstData = abData.IsFirstData;
                            isBreak = true;
                            break;
                        }
                    }

                    if (isBreak) break;
                }

                string strLine = string.Format("{0}|{1}|{2}|{3}", name, md5, size, isFirstData ? 1 : 0);
                sbContent.AppendLine(strLine);
            }

            IOUtil.CreateTextFile(strVersionFilePath, sbContent.ToString());
        }

        /// <summary>
        /// 创建VersionInfo.byte版本文件
        /// </summary>
        /// <param name="path"></param>
        /// <param name="sbContent"></param>
        private void CreateVersionByteFile(string path, StringBuilder sbContent)
        {
            MMO_MemoryStream ms = new MMO_MemoryStream();
            string str = sbContent.ToString().Trim();
            string[] arr = str.Split('\n');
            int len = arr.Length;
            ms.WriteInt(len);
            for (int i = 0; i < len; i++)
            {
                string[] arrInner = arr[i].Split('|');
                ms.WriteUTF8String(arrInner[0]);
                ms.WriteUTF8String(arrInner[1]);
                ms.WriteULong(ulong.Parse(arrInner[2]));
                ms.WriteByte(byte.Parse(arrInner[3]));
            }

            string filePath = path + "/VersionFile.bytes"; //版本文件路径
            byte[] buffer = ms.ToArray();
            buffer = ZlibHelper.CompressBytes(buffer);
            FileStream fs = new FileStream(filePath, FileMode.Create);
            fs.Write(buffer, 0, buffer.Length);
            fs.Close();
            fs.Dispose();
        }

        #endregion
    }
}


